#!/usr/bin/perl
# CCN::IPC::Ticket
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#

=pod

    CCN::IPC::Ticket allows to keep track of actions in-process (especially remote actions)
    and trigger local actions (via the CCN::IPC::Spool bus) when a number of responses have been received.

=cut

use strict; use warnings;

package CCN::IPC::Ticket;
use base qw(CCN::IPC::base);

=pod

    my $t = new CCN::IPC::Ticket         # brand new ticket
    my $t = new CCN::IPC::Ticket($id)    # existing ticket with id $id

=cut

sub initialize
{
    my $self = shift;
    my ($id) = @_;

    my $db = $self->get_db();
    
    if( defined $id )
    {
        $self->{id} = $id;
    }
    else
    {
        # $db->sql_begin();
        # my $new_pk = $db->sql_get(q(SELECT CASE COUNT(*) WHEN 0 THEN 1 ELSE MAX(pk)+1 END FROM tickets));
        $db->sql_do('INSERT INTO tickets (completed,label,started) VALUES (?,?,?)',0,"New ticket",time())
            or $db->dbh_die('CCN::IPC::Ticket::initialize');
        # $db->sql_commit();
        my $new_pk = $db->last_insert_id();
        
        $self->{id} = $new_pk;
    }
}

=pod
    
    $t -> ticket()

        return a ticket number that can be send to a remote party.
=cut

sub ticket() { return shift -> {id} };

=pod
    $t -> add_action  ($after,$action)
    
        $action will be triggered when $after replies have been received for $ticket

        add_action can be used asynchronously: the action will be triggered right away if
        at least $after replies have already been received.

=cut

sub add_action($$)
{
    my $self = shift;
    my ($after,$action) = @_;

    my $db = $self->get_db();
    $db->sql_begin();

    # Just run the action if we've already completed at least that many items.
    if( $db->sql_get(<<'SQL',$self->ticket) >= $after )
        SELECT completed FROM tickets WHERE pk = ?
SQL
    {
        $self->submit_action($action);
    }
    # Otherwise spool the action for later.
    else
    {
        $db->sql_do(<<'SQL',$self->ticket,$after,$action);
            INSERT INTO when_completed (ticket,completed,action) VALUES (?,?,?);
SQL
    }
    $db->sql_commit();
    
    return $self;
}

sub submit_action($)
{
    my $self = shift;
    $self->die(ref($self).' does not implement submit_action(..).');
}

sub add_context($$)
{
    my $self = shift;
    my ($key,$value) = @_;
    my $db = $self->get_db();
    $db->sql_do(<<'SQL',$self->ticket,$key,$value);
        INSERT INTO ticket_context (ticket,key,value) VALUES (?,?,?);
SQL
    return $self;
}

sub retrieve_context($)
{
    my $self = shift;
    my ($key) = @_;
    my $db = $self->get_db();
    return $db->sql_get(<<'SQL',$self->ticket,$key);
        SELECT value FROM ticket_context WHERE ticket = ? AND key = ?;
SQL
}

sub set_label
{
    my $self = shift;
    my $label = join(' ',@_);
    my $db = $self->get_db();
    $db->sql_do(<<'SQL',$label,$self->ticket);
        UPDATE tickets SET label = ? WHERE pk = ?;
SQL
    return $self;
}

sub get_label()
{
    my $self = shift;
    my $db = $self->get_db();
    return $db->sql_get(<<'SQL',$self->ticket);
        SELECT label FROM tickets WHERE pk = ?;
SQL
}

=pod

    my $t = new CCN::IPC::Ticket($id)
    $t->completed();
    
        This is used in ticket-completed.pl to add a "completed" notification.

=cut

sub completed
{
    my $self = shift;

    my $db = $self->get_db();
    $db->sql_begin();
    # Increment the count of completion received.
    $db->sql_do(<<'SQL',$self->ticket) or return $db->sql_rollback;
        UPDATE tickets SET completed = completed + 1 WHERE pk = ?
SQL

    # Trigger the actions.
    $db->sql_do_for(<<'SQL',$self->ticket,$self->ticket,
        SELECT action FROM when_completed
        WHERE ticket = ? AND completed = (SELECT completed FROM tickets WHERE pk = ?)
SQL
    sub
    {
        my $action = shift;
        $self->submit_action($action);
    });
    $db->sql_commit();
    return $self;
}

sub clear_ticket
{
    my $self = shift;
    my $db = $self->get_db();
    $db->sql_begin();
    $db->sql_do(<<'SQL',$self->ticket) or return $db->sql_rollback;
        DELETE FROM tickets WHERE pk = ?
SQL
    $db->sql_do(<<'SQL',$self->ticket) or return $db->sql_rollback;
        DELETE FROM when_completed WHERE ticket = ?
SQL
    $db->sql_do(<<'SQL',$self->ticket) or return $db->sql_rollback;
        DELETE FROM ticket_context WHERE ticket = ?
SQL
    $db->sql_commit();
    return $self;
}

=pod

    $t -> count_completed()
    
        Indicates whether the ticket has been completed or not, and if it has been
        completed, how many responses have been received.

=cut

sub count_completed()
{
    my $self = shift;
    my $db = $self->get_db();
    return $db->sql_get(q(SELECT completed FROM tickets WHERE pk = ?),$self->ticket); 
}

1;